/**
 * @description 上传文件
 */

/**
 * aws-sdk
 * 资源地址 https://sdk.amazonaws.com/js/aws-sdk-2.1665.0.min.js
 * 
 * DOMParser
 * 资源地址 https://github.com/pagnotta/domparser_in_webworker
 * 
 * S3
 * 文档地址 https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/API/Welcome.html
 * 文档地址 https://min.io/docs/minio/linux/developers/javascript/API.html
 *
 * importScripts
 * 文档地址 https://developer.mozilla.org/zh-CN/docs/Web/API/WorkerGlobalScope/importScripts
 *
 * worker
 * 文档地址 https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API/Using_web_workers
 */

// worker内不能使用window对象
var window = self;

// domparser
importScripts("/utils/domparser_bundle.js");
// worker内没有DOMParser
var DOMParser = xmldom.DOMParser;

// aws-sdk
importScripts("/utils/aws-sdk.min.js");

// 文件状态
const FileStatus = {
  waiting: "waiting", //等待上传
  success: "success", //上传成功
  failed: "failed", // 上传失败
  cancel: "cancel", // 取消
  uploading: "uploading", // 上传中
};

/**
 * @description uuid
 * @returns {string}
 */
const getCryptoUuid = () => {
  if (typeof crypto === "object") {
    if (typeof crypto.randomUUID === "function") {
      return crypto.randomUUID();
    }
    if (
      typeof crypto.getRandomValues === "function" &&
      typeof Uint8Array === "function"
    ) {
      const callback = (c) => {
        const num = Number(c);
        return (
          num ^
          (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (num / 4)))
        ).toString(16);
      };
      return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, callback);
    }
  }
  let timestamp = new Date().getTime();
  let perforNow =
    (typeof performance !== "undefined" &&
      performance.now &&
      performance.now() * 1000) ||
    0;
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
    let random = Math.random() * 16;
    if (timestamp > 0) {
      random = (timestamp + random) % 16 | 0;
      timestamp = Math.floor(timestamp / 16);
    } else {
      random = (perforNow + random) % 16 | 0;
      perforNow = Math.floor(perforNow / 16);
    }
    return (c === "x" ? random : (random & 0x3) | 0x8).toString(16);
  });
};

/**
 * @description 创建S3实例
 * @returns {AWS.S3}
 */
const createS3 = () => {
  const accessKeyId = "minioadmin"; // 账号
  const secretAccessKey = "admin123"; // 密码
  const endpoint = "http://139.196.79.129:9000"; // minio地址
  const region = "ap-northeast-1"; //  AWS 服务端点 文档地址 https://docs.aws.amazon.com/zh_cn/general/latest/gr/rande.html
  const s3ForcePathStyle = true;
  const signatureVersion = "v4";
  return new AWS.S3({
    accessKeyId,
    secretAccessKey,
    endpoint,
    region,
    s3ForcePathStyle,
    signatureVersion,
  });
};

/**
 * @description 上传管理
 */
class UploadManage {
  #bucket; // 桶名
  #s3 = createS3(); // s3 实例
  #fileList = []; // 文件列表
  #events = []; // 事件响应
  #uploader = new Map(); // 上传实例

  /**
   * @param {{bucket:string;}} param0
   */
  constructor({ bucket }) {
    this.#bucket = bucket;
  }

  /**
   * @description
   * @param {FileList} fileList
   */
  setFileList(fileList) {
    this.#fileList = [];
    for (let index = 0; index < fileList.length; index++) {
      const file = fileList.item(index);
      if (file) {
        this.#fileList.push({
          originFile: file,
          status: FileStatus.waiting,
          percent: 0,
        });
      }
    }
  }

  /**
   * @description 监听事件
   * @param {"onSucess" | "onError" | "onFailed" | "onChange" | "onLoading" | "onProgress" | "onAbort"} event
   * @param {(e:any)=>void} listener
   */
  on(event, listener) {
    this.#events.push({
      listener,
      event,
    });
  }

  /**
   * @description 关闭事件
   * @param {"onSucess" | "onError" | "onFailed" | "onChange" | "onLoading" | "onProgress" | "onAbort"} event
   */
  off(event) {
    this.#events = this.#events.filter((item) => item.event !== event);
  }

  /**
   * @description 触发事件
   * @param {"onSucess" | "onError" | "onFailed" | "onChange" | "onLoading" | "onProgress" | "onAbort"} event
   * @param {any} data
   */
  #emit(event, data) {
    this.#events.forEach((item) => {
      if (item.event === event) {
        item.listener(data);
      }
    });
  }

  /**
   * @description 上传文件
   * @param {{originFile:File;status:string;percent:number;abort:()=>void|null;}} file
   */
  #upload(file) {
    const params = {
      Bucket: this.#bucket,
      Key: file.originFile.webkitRelativePath,
      Body: file.originFile,
      ContentType: file.originFile.type,
    };
    const uuid = getCryptoUuid();
    const uploader = this.#s3.upload(params);
    this.#uploader.set(uuid, uploader);
    file.abort = uploader.abort;

    uploader.on("httpUploadProgress", (p) => {
      file.percent = Number(((p.loaded / p.total) * 100).toFixed(0));
    });

    uploader.send((err, data) => {
      if (err) {
        file.status = FileStatus.failed;
        this.#emit("onFailed", err);
      } else {
        file.status = FileStatus.success;
        this.#emit("onSucess", data);
      }
      file.abort = null;
      this.#uploader.delete(uuid);
      this.#pushUpload();
    });
  }

  /**
   * @description 取消全部上传
   */
  abort() {
    this.#fileList.forEach((file) => {
      if (file.status === FileStatus.waiting) {
        file.status = FileStatus.cancel;
      }
    });
    for (const [_, uploader] of this.#uploader.entries()) {
      uploader.abort();
    }
    this.#uploader.clear();
  }

  /**
   * @description 推入上传
   */
  #pushUpload() {
    for (let index = 0; index < this.#fileList.length; index++) {
      const file = this.#fileList[index];
      if (file.status === FileStatus.waiting) {
        file.status = FileStatus.uploading;
        this.#upload(file);
        break;
      }
    }
  }

  /**
   * @description 开始上传
   */
  start() {
    if (this.#fileList.length === 0) {
      this.#emit("onError", "没有可上传的文件");
    } else {
      this.#fileList.slice(0, 3).forEach((file) => {
        file.status = FileStatus.uploading;
        this.#upload(file);
      });
    }
  }
}

const actions = {
  uploads: "uploads",
  abort: "abort",
};

// 检测有无S3
if (AWS && AWS.S3) {
  // 上传管理实例
  const uploadManage = new UploadManage({ bucket: "log" });

  // 上传成功
  uploadManage.on("onSucess", (e) => {
    console.log(e, "onSucess");
  });

  // 上传失败
  uploadManage.on("onFailed", (e) => {
    console.log(e, "onFailed");
  });

  // 上传进度（总？单个文件？）
  uploadManage.on("onProgress", (e) => {});

  // 监听通信
  onmessage = (e) => {
    const { action, payload } = e.data;

    switch (action) {
      case actions.uploads:
        uploadManage.setFileList(payload);
        uploadManage.start();
        break;
      case actions.abort:
        uploadManage.abort();
        break;
      default:
        break;
    }
  };
}
